package com.timber.util;

import java.util.Enumeration;
import java.util.Vector;

import javax.swing.SwingConstants;
import javax.swing.event.TableModelEvent;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;

import pv.jfcx.PVTableRenderer;

import com.f1j.swing.JBook;

public class SLBook extends JBook implements SLTableMenuConstants
       //TableModelListener
       //TableColumnModelListener
       //ListSelectionListener,
       //CellEditorListener
{


    protected TableColumnModel  columnModel;

    private SLTableModel model = null;

    public SLBook() {
        super();
    }

    public SLBook(SLTableModel m) {
        super();
        this.model = m;

        constructor();

        if (model.getRowCount() > 0)
           setDataVector(model.getDataVector());
    }

    private void constructor()
    {
        setAutoRecalc(false);
        setShowEditBar(false);

        redoHeaders();

        initRowStuff();

        if (model == null)
           return;

        //model.removeTableModelListener(this);

        //model.addTableModelListener(this);

/*
        model.addTableModelListener(
            new TableModelListener() {
                public void tableChanged(TableModelEvent e)
                {

                }
            }
        );
        */
    }

    public void setDataVector(Vector v)
    {
        if (model != null)
        {
            model.setTableData( v );
        }
    }

    public void setData(Vector v)
    {
        clear();

        Object cols [] = model.getColumnList().toArray();
        Object vec [] = v.toArray();

        SLColumnDefinition cd = null;
        PVTableRenderer renderer = null;
        Vector values = null;
        java.awt.Component comp = null;

        boolean parsed = (vec[0] instanceof Vector);

        Util.log("[SLBook.setData()] received vector: " + parsed);

        for(int i = vec.length-1; i > -1; i--)
        {
            if (parsed)
               values = (Vector) vec[i];
            else
               values = (Vector) model.genTableRowVector( vec[i] );

            if (values == null)
               continue;

            Object obj = null;
            String str = null;

            for(int j = values.size()-1; j > -1 ; j--)
            {
                cd = (SLColumnDefinition) cols[j];
                renderer = (PVTableRenderer) cd.getRenderer();

                obj = values.get(j);

                try{
                    if (model != null &&
                       renderer != null && renderer instanceof DefaultSLObjectRenderer)
                    {
                        DefaultSLObjectRenderer slr = (DefaultSLObjectRenderer)renderer;
                        slr.setColorsAndFont(model, i, j);

                        com.f1j.ss.CellFormat cf = getCellFormat(i,j,i,j);
                        // Foreground
                        if (slr.getForeground() != null)
                           cf.setFontColor( JBook.color2RGB(slr.getForeground()) );
                        // Background
                        if (slr.getBackground() != null)
                        {
                           cf.setPattern( cf.ePatternSolid );
                           cf.setPatternBG( slr.getBackground().getRGB() );
                        }

                        switch(slr.getHorizontalAlignment())
                        {
                            case SwingConstants.CENTER:
                                 cf.setHorizontalAlignment( cf.eHorizontalAlignmentCenter );
                                 break;
                            case SwingConstants.RIGHT:
                                 cf.setHorizontalAlignment( cf.eHorizontalAlignmentRight );
                                 break;
                            case SwingConstants.LEFT:
                            default:
                                 cf.setHorizontalAlignment( cf.eHorizontalAlignmentLeft );
                        }
                    }

                    if (obj != null)
                    {
                        //Util.log("[SLBook.setData()] Formatting Object of " + obj.getClass().getName());

                        if (renderer != null && renderer.getFormat() != null)
                        {
                           str = renderer.getFormat().format( obj );
                        } else {
                           str = String.valueOf( obj );
                        }
                    }
                    else
                        str = "";

                    setText( i, j, str);

                }catch(Exception ex)
                {
                    if (obj != null)
                       Util.error("[SLBook.setData()] Formating Object of " + obj.getClass().getName());
                    Util.error("[SLBook.setData()]", ex);
                }finally{
                    obj = null;
                    str = null;
                    renderer = null;
                }
            }
        }

        if (true)
           resizeHeaders();
    }

    public void setTableModel(SLTableModel dataModel) {
        if (model == null) {
            throw new IllegalArgumentException("Cannot set a null TableModel");
	}
        if (this.model != dataModel) {
	    TableModel old = this.model;
            if (old != null) {
                //old.removeTableModelListener(this);
	    }
            this.model = dataModel;
            //dataModel.addTableModelListener(this);

            // If this method is called from the JTable constructor,
            // the column model will be null. In this case we can't use
            // the usual methods to update the internal state. In all other
            // cases, use the usual tableChanged() method to reconfigure
            // the JTable for the new model.
            //if (getColumnModel() != null) {
            //    tableChanged(new TableModelEvent(dataModel, TableModelEvent.HEADER_ROW));
            //}
	    firePropertyChange("model", old, dataModel);
        }
    }

    public SLTableModel getSLTableModel() {return model;}

    /**
     * Sets the column model for this table to <code>newModel</code> and registers
     * for listener notifications from the new column model. Also sets
     * the column model of the <code>JTableHeader</code> to <code>columnModel</code>.
     *
     * @param   columnModel        the new data source for this table
     * @exception IllegalArgumentException      if <code>columnModel</code> is <code>null</code>
     * @see     #getColumnModel
     * @beaninfo
     *  bound: true
     *  description: The object governing the way columns appear in the view.
     */
/*
    public void setColumnModel(TableColumnModel columnModel) {
        if (columnModel == null) {
            throw new IllegalArgumentException("Cannot set a null ColumnModel");
        }
        TableColumnModel old = this.columnModel;
        if (columnModel != old) {
            if (old != null) {
                old.removeColumnModelListener(this);
	    }
            this.columnModel = columnModel;
            columnModel.addColumnModelListener(this);

            // Set the column model of the header as well.
            if (tableHeader != null) {
                tableHeader.setColumnModel(columnModel);
            }

	    firePropertyChange("columnModel", old, columnModel);
            resizeAndRepaint();
        }
    }
*/
    /**
     * Returns the <code>TableColumnModel</code> that contains all column information
     * of this table.
     *
     * @return  the object that provides the column state of the table
     * @see     #setColumnModel
     */
     /*
    public TableColumnModel getColumnModel() {
        return columnModel;
    }
*/

    public void clear()
    {
        try{
 	       clearRange(0, 0, model.getRowCount(), model.getColumnCount(), JBook.eClearAll);
        } catch(Exception ex) {}
    }

    public void redoHeaders()
    {
        initColStuff();
        createDefaultColumnsFromModel();
        //getTable().updateSortIcons();
    }

    protected void createDefaultColumnsFromModel()
    {
        if (model == null)
           return;

        try {
            Vector colDefs = model.getColumnList();
            for(int i = 0; i < colDefs.size(); i++)
            {
                setColText(i, ((SLColumnDefinition)colDefs.get(i)).getDisplayLabel() );
            }
        } catch(com.f1j.util.F1Exception ex)
        {

        }
    }

    protected void initColStuff()
    {
        try
        {
            setMinCol(0);
            if (model != null && model.getColumnCount() > 1)
               setMaxCol(model.getColumnCount()-1);

            resizeHeaders();

        }catch(com.f1j.util.F1Exception ex)
        {
            Util.error("[SLBook.initColStuff()]", ex);
        }
    }

    public void resizeHeaders()
    {
        if (model == null)
            return;
        try
        {

 //            FontMetrics hdrFm = getFontMetrics( getHeaderFont() );
            Vector cols = model.getColumnList();
            //int maxHdrLines = 1;

            for( int i = 0; i < cols.size(); ++i )
            {
              SLColumnDefinition cd = ( ( SLColumnDefinition )cols.elementAt( i ) );

              //String dispNam = cd.getDisplayLabel();

              int w = cd.getDisplayWidth();
              setHeaderWidth( w*256 );
              setColWidth(i, w*256);

              //int hdrLines = getHeaderLinesNWidestString( dispNam );
              //maxHdrLines = ( hdrLines > maxHdrLines ) ? hdrLines : maxHdrLines;
            }

            //setHeaderHeight( maxHdrLines * ( hdrFm.getHeight() + 4 ) );
        }catch(com.f1j.util.F1Exception ex)
        {
            Util.error("[SLBook.resizeHeaders()]", ex);
        }

    }

    protected void initRowStuff()
    {
        try {
            setShowRowHeading(false);

            setMinRow(0);
            if (model != null && model.getRowCount() > 1)
               setMaxRow(model.getRowCount()-1);
        }catch(com.f1j.util.F1Exception ex)
        {
            Util.error("[SLBook.initRowStuff()]", ex);
        }
    }


    private int _zeroWidth = 0;
    public void tableToBookHeaders(SLTableView view, int zeroWidth)
    {
        try {
            Util.log("[SLBook.tableToBookHeaders()] running");

            if (_zeroWidth == 0)
            {
                _zeroWidth = zeroWidth;
            }

            TableColumnModel cm = view.getSLTable().getColumnModel();

            int i = 0;
            int w = 0; int ow = 0;
            for(Enumeration en = cm.getColumns(); en.hasMoreElements(); i++)
            {
                TableColumn tc = (TableColumn) en.nextElement();

                ow = tc.getWidth();
                w = getColWidth(i);
                tc.setPreferredWidth( (int)(w / 256 * _zeroWidth) );
                tc.setWidth(          (int)(w / 256 * _zeroWidth) );

                Util.log("[SLBook.tableToBookHeaders()] COL " + i + " FROM: " + ow + " TO: " + ((int)w / 256 * _zeroWidth));
            }
        }catch(com.f1j.util.F1Exception ex)
        {
            Util.error("[SLBook.tableToBookHeaders()]", ex);
        }
    }

/*
    public void clearSelection() {

    }
*/
/*
    public void tableChanged(TableModelEvent e) {

        if (e == null || e.getFirstRow() == TableModelEvent.HEADER_ROW) {
            // The whole thing changed
            redoHeaders();
            Util.log("[SLBook.tableChanged()] HEADER_ROW");
            resizeAndRepaint();
            return;
        }

        Util.log("[SLBook.tableChanged()] setData");
        this.setData( model.getDataVector() );

        resizeAndRepaint();
    }
    */
/*
    public void tableChanged(TableModelEvent e) {
        if (e == null || e.getFirstRow() == TableModelEvent.HEADER_ROW) {
            // The whole thing changed

            redoHeaders();

	    resizeAndRepaint();
            return;
        }

	// The totalRowHeight calculated below will be incorrect if
	// there are variable height rows. Repaint the visible region,
	// but don't return as a revalidate may be necessary as well.
	if (rowModel != null) {
	    repaint();
	}

        if (e.getType() == TableModelEvent.INSERT) {
            tableRowsInserted(e);
            return;
        }

        if (e.getType() == TableModelEvent.DELETE) {
            tableRowsDeleted(e);
            return;
        }

        int modelColumn = e.getColumn();
        int start = e.getFirstRow();
        int end = e.getLastRow();

        Rectangle dirtyRegion;
        if (modelColumn == TableModelEvent.ALL_COLUMNS) {
            // 1 or more rows changed
            dirtyRegion = new Rectangle(0, start * getRowHeight(),
                                        getColumnModel().getTotalColumnWidth(), 0);
        }
        else {
            // A cell or column of cells has changed.
            // Unlike the rest of the methods in the JTable, the TableModelEvent
            // uses the coordinate system of the model instead of the view.
            // This is the only place in the JTable where this "reverse mapping"
            // is used.
            int column = convertColumnIndexToView(modelColumn);
            dirtyRegion = getCellRect(start, column, false);
        }

        // Now adjust the height of the dirty region according to the value of "end".
        // Check for Integer.MAX_VALUE as this will cause an overflow.
        if (end != Integer.MAX_VALUE) {
	    dirtyRegion.height = (end-start+1)*getRowHeight();
            repaint(dirtyRegion.x, dirtyRegion.y, dirtyRegion.width, dirtyRegion.height);
        }
        // In fact, if the end is Integer.MAX_VALUE we need to revalidate anyway
        // because the scrollbar may need repainting.
        else {
            resizeAndRepaint();
        }
    }
*/
    /*
     * Invoked when rows have been inserted into the table.
     * <p>
     * Application code will not use these methods explicitly, they
     * are used internally by JTable.
     *
     * @param e the TableModelEvent encapsulating the insertion
     */

    private void tableRowsInserted(TableModelEvent e) throws com.f1j.util.F1Exception
    {
        int start = e.getFirstRow();
        int end = e.getLastRow();
        if (start < 0) {
            start = 0;
	}
	if (end < 0) {
	    end = getRowCount()-1;
	}

        // Adjust the selection to account for the new rows.
        setSelStartRow(start);
        setSelEndRow(end);


        resizeAndRepaint();
        /*
        int rHeight = 0;
        for(int i = start; i <= end; i++)
            height += getRowHeight(i);

        Rectangle drawRect = new Rectangle(0, start * rh,
                                        getColumnModel().getTotalColumnWidth(),
                                           (getRowCount()-start) * rh);

        revalidate();
        // PENDING(milne) revalidate calls repaint() if parent is a ScrollPane
	// repaint still required in the unusual case where there is no ScrollPane
        repaint(drawRect);
        */
    }

    /*
     * Invoked when rows have been removed from the table.
     * <p>
     * Application code will not use these methods explicitly, they
     * are used internally by JTable.
     *
     * @param e the TableModelEvent encapsulating the deletion
     */
    private void tableRowsDeleted(TableModelEvent e) {
        int start = e.getFirstRow();
        int end = e.getLastRow();
        if (start < 0) {
            start = 0;
	}
	if (end < 0) {
	    end = getRowCount()-1;
	}

        int deletedCount = end - start + 1;
        int previousRowCount = getRowCount() + deletedCount;
        // Adjust the selection to account for the new rows
	//selectionModel.removeIndexInterval(start, end);

	// If we have variable height rows, adjust the row model.
	//if (rowModel != null) {
	//    rowModel.removeEntries(start, deletedCount);
	//}

//        int rh = getRowHeight();
//        Rectangle drawRect = new Rectangle(0, start * rh,
//                                        getColumnModel().getTotalColumnWidth(),
//                                        (previousRowCount - start) * rh);

        revalidate();
        // PENDING(milne) revalidate calls repaint() if parent is a ScrollPane
	// repaint still required in the unusual case where there is no ScrollPane
//        repaint(drawRect);
    }


    /**
     * Equivalent to <code>revalidate</code> followed by <code>repaint</code>.
     */
    protected void resizeAndRepaint() {
        revalidate();
        repaint();
    }

    public int getRowCount()
    {
        return model.getRowCount();
    }
}